package org.summer.gateway.security;

import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.RequestPath;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.server.authorization.AuthorizationContext;
import org.springframework.stereotype.Component;
import org.springframework.web.util.pattern.PathPatternParser;
import org.summer.gateway.pojo.SecurityPath;
import org.summer.gateway.pojo.SecurityRole;
import org.summer.gateway.service.SecurityPathService;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

/**
 * 网关权限验证管理器
 */
@Log4j2
@Component
@RequiredArgsConstructor
public class GatewayAuthorizationManager implements ReactiveAuthorizationManager<AuthorizationContext> {
    private final SecurityPathService pathService;
    private final PathPatternParser pathPatternParser;
    private RequestPath requestPath;
    private HttpMethod requestMethod;
    @Override
    public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, AuthorizationContext context) {
        this.requestPath = context.getExchange().getRequest().getPath();
        this.requestMethod = context.getExchange().getRequest().getMethod();
        return authentication.flatMap(auth -> Flux.fromIterable(auth.getAuthorities())
                    .flatMap(e -> pathService.findListByRoleId(((SecurityRole)e).getId()))
                    .distinct()
                .any(this::checkPathAuthority)
                .map(AuthorizationDecision::new)
                .switchIfEmpty(Mono.just(new AuthorizationDecision(false)))
        );
    }

    /**
     * 用户请求路径和方法权限检查辅助方法
     */
    private boolean checkPathAuthority(SecurityPath path){
        boolean pathMatches = pathPatternParser.parse(path.getPattern()).matches(this.requestPath);
        if (!pathMatches){
            log.debug("用户路径权限验证未通过: {}", path);
            return false;
        }
        boolean methodMatches = "ANY".equals(path.getMethod()) || requestMethod.matches(path.getMethod());
        if (!methodMatches){
            log.debug("用户方法权限验证未通过: {}", path);
        }
        log.info("用户路径方法权限认证通过:{}", path);
        return methodMatches;
    }
}
